package com.dlna.client.activity;

import static com.dlna.client.util.Constant.DEBUG;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;

import com.dlna.client.util.Constant;
import com.dlna.client.util.Util;

public class SocketClient
{
    private static final String TAG = "SocketClient";
    
    //    private static final String ALL_HOST = "255.255.255.255";
    private static final String ALL_HOST = "224.5.0.7";
    
    private String serverIp = "";
    
    private static SocketClient socketService;
    
    private MulticastSocket /*controlSocket, */heartSocket;
    
    private ReceiveThread receiveHeart;
    
    private boolean isRun = false;
    
    private OnReceiveMessageListener receiveMessageListener;
    
    private static final ExecutorService sExecutor = Executors.newFixedThreadPool(2);
    
    private Handler mHandler = new Handler();
    
    public String getServerIp()
    {
        return serverIp;
    }
    
    public void setServerIp(String serverIp)
    {
        this.serverIp = serverIp;
    }
    
    private Runnable offLineRunnable = new Runnable()
    {
        
        @Override
        public void run()
        {
            
            if (DEBUG)
            {
                Log.e(TAG, "1����δ�յ��������,��Ϊ����ƻ�Ͽ���������");
            }
            if (receiveMessageListener != null)
            {
                receiveMessageListener.notifyOffline();
            }
            /**
             * ֹͣ����
             */
            //            stopHeartBeat();
            sendOffLineMessage();
        }
    };
    
    private Runnable noControlRunnable = new Runnable()
    {
        
        @Override
        public void run()
        {
            if (receiveMessageListener != null)
            {
                receiveMessageListener.notifyNoControl();
            }
        }
    };
    
    /**
     * ����������Ϣ
     */
    private void sendOffLineMessage()
    {
        
        sendMessage(Constant.MESSAGE_CHECK_OFFLINE_BODY,
                serverIp,
                Constant.MESSAGE_OFFLINE_ID);
    }
    
    public static SocketClient getInstance(Context context)
    {
        if (socketService == null)
        {
            socketService = new SocketClient();
        }
        return socketService;
    }
    
    public void setOnReceiveMessageListener(
            OnReceiveMessageListener receiveMessageListener)
    {
        this.receiveMessageListener = receiveMessageListener;
    }
    
    private SocketClient()
    {
        
    }
    
    public void listening()
    {
        clearAllMessage();
        initSocket();
    }
    
    public void clearAllMessage()
    {
        //		mHandler.removeCallbacks(noControlRunnable);
        mHandler.removeCallbacks(offLineRunnable);
    }
    
    //    private void startControlThread()
    //    {
    //        receiveControl = new ReceiveThread(controlSocket);
    //        receiveControl.start();
    //    }
    
    private void initSocket()
    {
        try
        {
            heartSocket = new MulticastSocket(Constant.PORT_CLIENT_HEART);
            //            controlSocket = new MulticastSocket(Constant.PORT_CLIENT_CONTROL);
            
            heartSocket.setLoopbackMode(true);
            InetAddress group = InetAddress.getByName(ALL_HOST);
            heartSocket.joinGroup(group);
            //            controlSocket.joinGroup(group);
            receiveHeart = new ReceiveThread(heartSocket);
            receiveHeart.start();
            //            startControlThread();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    /**
     * �Ͽ�����
     */
    public void disConnect()
    {
        sendOffLineMessage();
        clearAllMessage();
        stopCheckClient();
        stopHeartBeat();
        isRun = false;
        //        if (controlSocket != null)
        //        {
        //            controlSocket.close();
        //            controlSocket = null;
        //        }
        if (heartSocket != null)
        {
            heartSocket.close();
            heartSocket = null;
        }
        receiveHeart = null;
    }
    
    public void checkClientOnline()
    {
        sendMessage(Constant.MESSAGE_CLIENT_CHECK_ONLINE_BODY,
                ALL_HOST,
                Constant.MESSAGE_CHECK_ID);
    }
    
    private Timer timer = new Timer();
    
    private TimerTask timerTask, heartBeatTask;
    
    private int reSendNumber = 1;
    
    /**
     * ���ͻ����Ƿ�����
     */
    public void startCheckClient()
    {
        stopCheckClient();
        reSendNumber = 0;
        if (timerTask == null)
        {
            timerTask = new TimerTask()
            {
                @Override
                public void run()
                {
                    checkClientOnline();
                    reSendNumber++;
                    if (reSendNumber > Constant.RESEND_NUMBER)
                        stopCheckClient();
                }
            };
        }
        timer.schedule(timerTask,
                Constant.DELAY_MILLIS,
                Constant.INTERVAL_CHECK_ONLINE);
    }
    
    /**
     * �����κ�ֹͣ���
     * 
     * @param should
     */
    private void stopCheckClient()
    {
        if (timerTask != null)
        {
            timerTask.cancel();
            timerTask = null;
        }
    }
    
    /**
     * ��ʼ����
     */
    public void startHeartBeat()
    {
        stopHeartBeat();
        if (heartBeatTask == null)
            heartBeatTask = new TimerTask()
            {
                @Override
                public void run()
                {
                    sendMessage(Constant.MESSAGE_HEART_BEAT_BODY,
                            serverIp,
                            Constant.MESSAGE_HEART_ID);
                }
            };
        timer.schedule(heartBeatTask,
                Constant.DELAY_MILLIS,
                Constant.INTERVAL_HEART_BEAT);
    }
    
    /**
     * ֹͣ����
     */
    public void stopHeartBeat()
    {
        if (heartBeatTask != null)
        {
            heartBeatTask.cancel();
            heartBeatTask = null;
        }
    }
    
    /**
     * ������Ϣ���
     * 
     * @param message
     * @param host
     * @param type
     */
    public void sendMessage(byte[] message, String host, byte messageID)
    {
        if (!TextUtils.isEmpty(host))
        {
            sExecutor.execute(new Worker(message, host, messageID));
        }
        else
        {
            sExecutor.execute(new Worker(message, ALL_HOST, messageID));
        }
    }
    
    private class Worker implements Runnable
    {
        private byte[] message;
        
        private String host;
        
        private byte messageID;
        
        public Worker(byte[] message, String host, byte messageID)
        {
            this.message = message;
            this.host = host;
            this.messageID = messageID;
        }
        
        public void run()
        {
            sendMessageToServer(message, host, messageID);
        }
    }
    
    public void sendMessageToServer(byte[] message, String host, byte messageID)
    {
        if (heartSocket != null)
        {
            InetAddress addr;
            try
            {
                addr = InetAddress.getByName(host);
                ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + message.length + 1);
                addHeadTag(buffer);
                buffer.put(messageID);
                buffer.put(message);
                addTailTag(buffer);
                
                switch (messageID)
                {
                    case Constant.MESSAGE_HEART_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ��˷�������--------->");
                        }
                        break;
                    case Constant.MESSAGE_CHECK_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ��˷��ͼ����Ϣ--------->");
                        }
                        break;
                    
                    case Constant.MESSAGE_SYNCFILE_SUCCESS_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ��˷������ͬ���ɹ���Ϣ--------->");
                        }
                        break;
                    
                    case Constant.MESSAGE_SYNCFILE_FAIL_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ��˷������ͬ��ʧ����Ϣ--------->");
                        }
                        break;
                    default:
                        break;
                }
                DatagramPacket sendPacket = new DatagramPacket(buffer.array(),
                        buffer.capacity(), addr, Constant.PORT_SEVER_HEART);
                heartSocket.send(sendPacket);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    
    private class ReceiveThread extends Thread
    {
        private DatagramSocket serverSocket;
        
        ReceiveThread(DatagramSocket serverSocket)
        {
            this.serverSocket = serverSocket;
        }
        
        @Override
        public void run()
        {
            if (serverSocket == null)
            {
                return;
            }
            
            isRun = true;
            byte[] message = null;
            while (isRun)
            {
                try
                {
                    DatagramPacket receivePacket = new DatagramPacket(
                            new byte[Constant.MESSAGE_MAX_LENGTH],
                            Constant.MESSAGE_MAX_LENGTH);
                    serverSocket.receive(receivePacket);
                    
                    // get received data
                    byte data[] = receivePacket.getData();
                    
                    if (data != null)
                    {
                        message = Arrays.copyOfRange(data,
                                0,
                                receivePacket.getLength());
                        praseData(message, receivePacket, serverSocket);
                    }
                    
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
    
    private void praseData(byte[] message, DatagramPacket receivePacket,
            DatagramSocket serverSocket)
    {
        if (message.length < 3)
        {
            return;
        }
        try
        {
            if (hasHeadTag(message) && hasTailTag(message))
            {
                serverIp = receivePacket.getAddress().getHostAddress();
                /**
                 * �յ����ֹͣ���
                 */
                stopCheckClient();
                //                startHeartBeat();
                if (heartSocket == serverSocket)
                {
                    mHandler.removeCallbacks(offLineRunnable);
                    mHandler.postDelayed(offLineRunnable,
                            Constant.DELAY_OFFLINE_TIMEOUT);
                }
                //                else if (controlSocket == serverSocket)
                //                {
                //                    mHandler.removeCallbacks(noControlRunnable);
                //                    mHandler.postDelayed(noControlRunnable,
                //                            Constant.DELAY_NO_CONTROL_TIMEOUT);
                //                }
                ByteBuffer buffer = ByteBuffer.wrap(message);
                buffer.get();
                byte messageID = buffer.get();
                byte[] result = new byte[message.length - 3];
                buffer.get(result);
                switch (messageID)
                {
                    case Constant.MESSAGE_PICTURE_ID:
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.onReceiveMessage(Constant.MESSAGE_PICTURE_ID,
                                    result);
                        }
                        //                        String picURI = new String(result, "GBK");
                        //                        ArrayList<FileInfo> pictureList = new ArrayList<FileInfo>();
                        //                        pictureList.add(Util.GetFileInfo(picURI));
                        //                        Intent intent = new Intent(mContext,
                        //                                ImagePagerActivity.class);
                        //                        intent.putParcelableArrayListExtra(Extra.IMAGES,
                        //                                pictureList);
                        //                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        //                        mContext.startActivity(intent);
                        break;
                    case Constant.MESSAGE_VIDEO_ID:
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.onReceiveMessage(Constant.MESSAGE_VIDEO_ID,
                                    result);
                        }
                        //                        String videoURI = new String(result, "GBK");
                        //                        Intent videoIntent = new Intent(mContext,
                        //                                PlayerActivity.class);
                        //                        videoIntent.putExtra(Constant.PLATURL, videoURI);
                        //                        mContext.startActivity(videoIntent);
                        break;
                    case Constant.MESSAGE_HEART_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ�������Ϣ--------->�ظ�����Ӧ��");
                        }
                        sendMessage(result,
                                serverIp,
                                Constant.MESSAGE_HEART_ANSWER_ID);
                        break;
                    
                    case Constant.MESSAGE_HEART_ANSWER_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ����������Ӧ����Ϣ--------->");
                        }
                        break;
                    
                    case Constant.MESSAGE_CHECK_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ������Ϣ--------->�ظ����Ӧ��");
                        }
                        if (Arrays.equals(result,
                                Constant.MESSAGE_CHECK_ONLINE_BODY))
                        {
                            sendMessage(result,
                                    serverIp,
                                    Constant.MESSAGE_CHECK_ANSWER_ID);
                        }
                        
                    case Constant.MESSAGE_CHECK_ANSWER_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ����Ӧ����Ϣ--------->������--->ȡ���ͼ����Ϣ");
                        }
                        //                        stopCheckClient();
                        break;
                    case Constant.MESSAGE_CONTROL_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ�������Ϣ--------->��ʼ��ʱ-----�ظ�����Ӧ��");
                        }
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.notifyInControl();
                        }
                        //                        mHandler.postDelayed(noControlRunnable,
                        //                                Constant.DELAY_NO_CONTROL_TIMEOUT);
                        sendMessage(result,
                                serverIp,
                                Constant.MESSAGE_CONTROL_ANSWER_ID);
                        
                        break;
                    
                    case Constant.MESSAGE_NO_CONTROL_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ�ȡ�������Ϣ--------->�޻ظ�");
                        }
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.notifyNoControl();
                        }
                        break;
                    
                    case Constant.MESSAGE_SYNCFILE_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ����ͬ��������Ϣ------->�ظ����ͬ������Ӧ��");
                        }
                        sendMessage(result,
                                serverIp,
                                Constant.MESSAGE_SYNCFILE_ANSWER_ID);
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.notifySyncfile(result);
                        }
                        break;
                    
                    case Constant.MESSAGE_SYNCFILE_SUCCESS_ANSWER_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ����ͬ���ɹ�����Ӧ��------->������");
                        }
                        break;
                    case Constant.MESSAGE_SYNCFILE_FAIL_ANSWER_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ����ͬ��ȡ������Ӧ��------->������");
                        }
                        break;
                    
                    case Constant.MESSAGE_CHECKFILE_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ������Դ�ļ�����------->");
                        }
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.notifyCheckfile(result);
                        }
                        break;
                    
                    case Constant.MESSAGE_VIDEO_START_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ���Ƶ��ʼ����------->");
                        }
                        if (receiveMessageListener != null)
                        {
                            int pos = Util.bytes2Int(result, 0);
                            receiveMessageListener.notifyVideoStart(pos);
                        }
                        break;
                    case Constant.MESSAGE_VIDEO_PAUSE_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ���Ƶ��ͣ����------->");
                        }
                        if (receiveMessageListener != null)
                        {
                            int pos = Util.bytes2Int(result, 0);
                            receiveMessageListener.notifyVideoPause(pos);
                        }
                        break;
                    case Constant.MESSAGE_VIDEO_SEEKTO_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ���Ƶ��λ����------->");
                        }
                        
                        if (receiveMessageListener != null)
                        {
                            int pos = Util.bytes2Int(result, 0);
                            receiveMessageListener.notifyVideoSeekTo(pos);
                        }
                        break;
                    
                    case Constant.MESSAGE_VIDEO_STOP_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�ͻ����յ���Ƶֹͣ����------->");
                        }
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.notifyVideoStop();
                        }
                        break;
                    
                    case Constant.MESSAGE_SYNCFILE_CANCEL_ID:
                        if (DEBUG)
                        {
                            Log.e(TAG, "�յ�ȡ��ͬ����Դ����------->");
                        }
                        if (receiveMessageListener != null)
                        {
                            receiveMessageListener.notifySyncfileCancel();
                        }
                        break;
                    
                    default:
                        break;
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    private void addHeadTag(ByteBuffer buffer)
    {
        buffer.put(Constant.HEAD_TAG);
    }
    
    private void addTailTag(ByteBuffer buffer)
    {
        buffer.put(Constant.TAIL_TAG);
    }
    
    private boolean hasHeadTag(byte[] bytes)
    {
        return (bytes[0] == Constant.HEAD_TAG);
    }
    
    private boolean hasTailTag(byte[] bytes)
    {
        return Constant.TAIL_TAG == bytes[bytes.length - 1];
    }
    
    public interface OnReceiveMessageListener
    {
        void onReceiveMessage(byte messageID, byte[] message) throws Exception;
        
        void notifyOnline();
        
        void notifyOffline();
        
        void notifyInControl();
        
        void notifyNoControl();
        
        void notifySyncfile(byte[] message);
        
        void notifyCheckfile(byte[] message);
        
        void notifySyncfileCancel();
        
        void notifyVideoStart(int pos);
        
        void notifyVideoPause(int pos);
        
        void notifyVideoSeekTo(int pos);
        
        void notifyVideoStop();
    }
}
